/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.proxy.support;

import com.aduib.boot.common.proxy.MethodInterceptor;
import com.aduib.boot.common.proxy.Proxy;
import com.aduib.boot.common.util.ClassUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @description: AbstractProxy
 * @author: zzh
 * @date: 2021/10/25 15:02
 */
public abstract class AbstractProxy implements Proxy {

    private final Class<?> sourceClass;

    private final List<MethodInterceptor> methodInterceptorList;

    protected Object[] constructorArgs;
    protected Class<?>[] constructorArgTypes;
    private Object sourceObject;
    private Class<?>[] interfaces;
    private Class<?> superClass;
    private ClassLoader classLoader;

    @Deprecated
    private transient MethodInterceptor methodInterceptor;

    public AbstractProxy(Object targetObject) {
        this(targetObject.getClass());
        this.sourceObject = targetObject;
    }

    public AbstractProxy(Class<?> targetClass) {
    this.sourceClass = targetClass;
    this.methodInterceptorList = new ArrayList<>();
    initProxyInfo(targetClass);
  }

  private void initProxyInfo(Class<?> targetClass) {
    this.classLoader = targetClass.getClassLoader();
    this.interfaces = ClassUtils.getInterfaceClass(targetClass);
    if (interfaces.length == 0 && targetClass.isInterface()) {
      this.interfaces = new Class<?>[1];
      this.interfaces[0] = targetClass;
    }
    this.superClass = targetClass.isInterface() ? null : targetClass;
  }

    @Override
    public Object getSourceObject() {
        return this.sourceObject;
    }

    @Override
    public Object getProxy() {
        return getProxy(getClassLoader());
    }

    public void setConstructorArguments(Object[] constructorArgs, Class<?>[] constructorArgTypes) {
        if (constructorArgs == null || constructorArgTypes == null) {
            throw new IllegalArgumentException(
                    "Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
        }
        if (constructorArgs.length != constructorArgTypes.length) {
            throw new IllegalArgumentException(
                    "Number of 'constructorArgs' ("
                            + constructorArgs.length
                            + ") must match number of 'constructorArgTypes' ("
                            + constructorArgTypes.length
                            + ")");
        }
        this.constructorArgs = constructorArgs;
        this.constructorArgTypes = constructorArgTypes;
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        ClassLoader targetClassLoader = classLoader;
        if (!classLoader.equals(sourceClass.getClassLoader())) {
            targetClassLoader = sourceClass.getClassLoader();
        }
        return doGetProxy(targetClassLoader, sourceClass, interfaces, superClass, methodInterceptorList);
    }

  @Override
  public void addAspect(MethodInterceptor methodInterceptor) {
    methodInterceptorList.add(methodInterceptor);
  }

  /** @param methodInterceptor */
  @Override
  public void replaceInterceptor(MethodInterceptor methodInterceptor) {
    this.methodInterceptor = methodInterceptor;
  }

  /**
   * 创建代理对象
   *
   * @param classLoader
   * @param targetClass
   * @param interfaces
   * @param superClass
   * @param methodInterceptorList
   * @return
   */
  protected abstract Object doGetProxy(
      ClassLoader classLoader,
      Class<?> targetClass,
      Class<?>[] interfaces,
      Class<?> superClass,
      List<MethodInterceptor> methodInterceptorList);

  public Class<?> getSourceClass() {
    return sourceClass;
  }

  public Class<?>[] getInterfaces() {
    return interfaces;
  }

  public Class<?> getSuperClass() {
    return superClass;
  }

  public ClassLoader getClassLoader() {
    return classLoader;
  }

  @Deprecated
  public MethodInterceptor getMethodInterceptor(MethodInterceptor methodInterceptor) {
    if (Objects.isNull(this.methodInterceptor)) {
      return methodInterceptor;
    } else {
      return this.methodInterceptor;
    }
  }

  public List<MethodInterceptor> getAspectList() {
    return methodInterceptorList;
  }
}
